<?php
// +----------------------------------------------------------------------
// | SwiftPHP [ JUST DO ONE THING WELL ]
// +----------------------------------------------------------------------
// | Copyright (c) 2015-2017  http://swiftphp.zhongwenyu.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhongwenyu <zhongwenyu1987@163.com> <http://www.zhongwenyu.com>
// +----------------------------------------------------------------------
// | Times: 2017/2/16 14:46
// +----------------------------------------------------------------------
namespace swift;

class Loader{
    // 实例化
    public static $instance = [];
    // PSR-4
    private static $fallbackDirsPsr4  = [];
    private static $prefixDirsPsr4    = [];
    private static $prefixLengthsPsr4 = [];
    // 命名空间别名
    protected static $namespaceAlias = [];

    //反射类
    protected static $invokeClass = null;

    // 注册命名空间
    public static function addNamespace($namespace, $path = '')
    {
        if (is_array($namespace)) {
            foreach ($namespace as $prefix => $paths) {
                self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
            }
        } else {
            self::addPsr4($namespace . '\\', rtrim($path, DS), true);
        }
    }

    //系统自动加载
    public static function autoload($class){
        if ($file = self::findFile($class)) {
            include $file;
            return true;
        }
    }

    /**
     * 查找文件
     * @param $class
     * @return bool
     */
    private static function findFile($class){
        // 查找 PSR-4
        $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
        $first = $class[0];
        if (isset(self::$prefixLengthsPsr4[$first])) {
            foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
                if (0 === strpos($class, $prefix)) {
                    foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
                        if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
                            return $file;
                        }
                    }
                }
            }
        }
    }

    // 注册自动加载机制
    public static function register($autoload = '')
    {
        // 注册系统自动加载
        spl_autoload_register($autoload ?: 'swift\\Loader::autoload', true, true);

        // 注册命名空间定义
        self::addNamespace([
            'swift'    => LIB_PATH . 'swift' . DS,
            'traits'    => LIB_PATH . 'traits' . DS,
        ]);
    }

    // 添加Psr4空间
    private static function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                self::$fallbackDirsPsr4 = array_merge(
                    (array) $paths,
                    self::$fallbackDirsPsr4
                );
            } else {
                self::$fallbackDirsPsr4 = array_merge(
                    self::$fallbackDirsPsr4,
                    (array) $paths
                );
            }
        } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            self::$prefixDirsPsr4[$prefix]                = (array) $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            self::$prefixDirsPsr4[$prefix] = array_merge(
                (array) $paths,
                self::$prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            self::$prefixDirsPsr4[$prefix] = array_merge(
                self::$prefixDirsPsr4[$prefix],
                (array) $paths
            );
        }
    }

    /**
     * 实例化（分层）控制器 格式：[模块名/]控制器名
     * @param string $name         资源地址
     * @param string $layer        控制层名称
     * @param bool   $appendSuffix 是否添加类名后缀
     * @param string $empty        空控制器名称
     * @return Object|false
     * @throws ClassNotFoundException
     */
    public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = ''){
        // 获取当前模块
        $module = Request::instance()->module();
        $class = self::parseClass($module, $layer, $name, $appendSuffix);
        if(class_exists($class)){
            return self::invokeClass($class);
        }
    }

    /**
     * 解析应用类的类名
     * @param string $module 模块名
     * @param string $layer  层名 controller model ...
     * @param string $name   类名
     * @param bool   $appendSuffix
     * @return string
     */
    public static function parseClass($module, $layer, $name, $appendSuffix = false){
        return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $name;
    }

    /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param string    $class 类名
     * @param array     $vars  变量
     * @return mixed
     */
    public static function invokeClass($class, $vars = []){
        if(is_null(self::$invokeClass)){
            $reflect = new \ReflectionClass($class);
            $construct = $reflect->getConstructor();
            if($construct){
                $args = self::bindParams($construct, $vars);
            }else{
                $args = [];
            }
            self::$invokeClass = $reflect->newInstanceArgs($args);
        }
        return self::$invokeClass;
    }

    /**
     * 调用反射执行类的方法 支持参数绑定
     * @access public
     * @param string|array $method 方法
     * @param array        $vars   变量
     * @return mixed
     */
    public static function invokeMethod($method, $vars = []){
        if(is_array($method)){
            $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
            $reflect = new \ReflectionMethod($method[0],$method[1]);
        }else{
            // 静态方法
            $reflect = new \ReflectionMethod($method);
        }
        $args = self::bindParams($reflect,$vars);
        return $reflect->invokeArgs(isset($class) ? $class : null , $args);
    }

    /**
     * 绑定参数
     * @access public
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
     * @param array                                 $vars    变量
     * @return array
     */
    private static function bindParams($reflect, $vars = []){
        $vars = Request::instance()->param();
        $args = [];
        reset($vars);   //重置数组指针到第一个
        $type = key($vars) === 0 ? 1 : 0;   //返回当前指针位置的键名，判断是索引数组还是关联数组
        if($reflect->getNumberOfParameters() > 0){    //获取被反射函数的参数数目
            $params = $reflect->getParameters();   //返回一组ReflectionParameter 对象，表示被反射函数的每一参数
            foreach ($params as $param) {
                $name  = $param->getName();   //返回参数名称
                if(1 === $type && !empty($vars)){
                    $args[] = array_shift($vars);
                }elseif(0 === $type && isset($vars[$name])){
                    $args[] = $vars[$name];
                }elseif($param->isDefaultValueAvailable()){   //检查参数是否有默认值，有返回true，没有返回false
                    $args[] = $param->getDefaultValue();
                }else{
                    throw new \Exception("缺少参数 ".$name);
                }
            }
        }
        return $args;
    }

    /**
     * 实例化验证类 格式：[模块名/]验证器名
     * @param string $name         资源地址
     * @param string $layer        验证层名称
     * @return Object|false
     * @throws ClassNotFoundException
     */
    public static function validate($name = '', $layer = 'validate'){
        $name = $name ?: Config::get('default_validate');
        if(empty($name)){
            return new Validate();
        }
        $guid = $name . $layer;
        if (isset(self::$instance[$guid])) {
            return self::$instance[$guid];
        }
        if(strpos($name , '\\')){
            $validate = new $name();
        }else{
            $class = self::parseClass('' , $layer , $name);
            if(class_exists($class)){
                $validate = new $class();
            }else{
                $class = self::parseClass(Request::instance()->module() , $layer , $name);
                if(class_exists($class)){
                    $validate = new $class();
                }else{
                    throw new \Exception("$name 类不存在");
                }
            }
        }
        self::$instance[$guid] = $validate;
        return $validate;
    }

    /**
     * 初始化类的实例
     * @return void
     */
    /*public static function clearInstance()
    {
        self::$instance = [];
    }*/
}